#pragma once
#include <unordered_map>
#include <list>
#include <filesystem>
#include <vector>
#include <utility>
#if defined(__linux__)
#include <sys/uio.h>
#endif
#include <cstring>
#include <string_view>
#include <any>
#if defined(__linux__)
#include "linux_pb/frpc.pb.h"
#endif
#if defined(_WIN32)
#include "win_pb/frpc.pb.h"
#endif
#include "rpc_cmd.h"
#include "status_code.h"
#include <cppbase/logging.h>
#include <vector>
#define FRPC_START_BYTES 0x8877

namespace Frpc
{
#if defined(_WIN32)
    struct iovec {
        void* iov_base;
        size_t iov_len;
};
#endif
    #pragma pack(push, 2)
    struct Head
    {                                            
        uint16_t start_bytes ; //每个msg的起始8字节，用于标识协议
        Command cmd;
        private:
        uint32_t _length;//flowing bytes size without head
        public:
        uint16_t request_seq;
        //uint8_t reply_seq; use meta if chunked or compress
        void set_meta_bit(){_length|=0x80000000;}//set highest bit
        void clear_meta_bit(){_length&=0x7FFFFFFF;}//set highest bit
        uint32_t content_length(){return  _length&0x7FFFFFFF;} 
        void set_content_length(uint32_t v){if(_length&0x80000000) {v|=0x80000000 ;_length=v;}else _length=v; }
        bool has_meta(){return _length&0x80000000;}// 2 bytes at begin of content is meta length
        
    };
#pragma pack(pop)
    class Msg
    {

      protected:
        int headcontent_used;
        
        int parse_stage=0;
        struct iovec headcontent_buf;
        struct iovec meta_buf;
        std::vector<struct iovec> attachment;
        Meta meta;
        Head* head(){return (Head*)headcontent_buf.iov_base;};
     //   uint16_t meta_size(){return *(uint16_t*)((char*)headcontent_buf.iov_base+sizeof(Head));}
    public:
        Msg()
        {
            //
            headcontent_buf.iov_len=64;
            headcontent_used=sizeof(Head);
            headcontent_buf.iov_base=malloc(headcontent_buf.iov_len);
            
            memset(&meta_buf, 0, sizeof(struct iovec));
            head()->start_bytes=FRPC_START_BYTES;
        }
       
        
        int head_size(){return sizeof(Head);}
       // bool read_head(char* buf){memcpy(headcontent_buf.iov_base,buf,sizeof(Head));return head()->start_bytes==FRPC_START_BYTES;}
       // bool read_meta(char *buf,int size){if(!head()->has_meta())return true; return meta.ParseFromArray(buf,size);}//first 2 bytes shows the meta size
        bool is_login_cmd()
        {
            return ((uint16_t)head()->cmd & 0xff00) == uint16_t(Command::LoginService);
        };
        void set_request_seq(uint16_t v) { head()->request_seq=v; };
        uint16_t request_seq() { return head()->request_seq; };

        //read write meta data
        //void set_status(uint32_t v) { meta.set_status(v); };
        //Status status() { return Status(meta.status()); };
        //int status_as_int() { return meta.status(); };
       // void set_status(Status v) { meta.set_status(uint32_t(v)); };
        //uint32_t meta_status() { return meta.status(); };
        void set_command(uint16_t v) {head()->cmd=(Command)v; };
        void set_command(Command v) { head()->cmd=v; };
        int get_cmd(){return int(head()->cmd);}


     
//input handler
        
        int parse_full_stream(char *buf, size_t *size);
        uint32_t recieced_contentsize() { return headcontent_used-sizeof(Head); };
        std::string_view input() { return std::string_view((char *)headcontent_buf.iov_base+sizeof(Head), headcontent_used-sizeof(Head)); };
        void reserve_content_buf(int size)
        {   
            size+=sizeof(Head);
            if(size<=headcontent_buf.iov_len)
                return;
            headcontent_buf.iov_len = size;
            if (headcontent_buf.iov_base)
            {
                headcontent_buf.iov_base = realloc(headcontent_buf.iov_base, size);   
            }
            else
                headcontent_buf.iov_base = malloc(size);
            if(!headcontent_buf.iov_base)
            {
                FATAL("allocate memory failed.");
               
            }

        };
        void append_input_copy(char *buf, int size);
        
        bool bad_input();

        //read write content data
        void set_output(std::string &&s);

        // void set_status_output(Status v,char* buf){set_output(buf,strlen(buf));set_status(v);};
        void set_ok_output(std::string &&s = std::string());
        void set_ok_output(uint16_t );
        void set_ok_output(uint32_t );
        void set_ok_output(uint16_t ,uint16_t);
        void set_cmd_output(Command cmd,std::string &&s = std::string());
        void set_bad_output(Status code, std::string &&s = std::string());
       // void set_output(uint64_t v, int size);
        //void set_output(uint64_t v, int size);
        void add_output_nocopy(void *buf, size_t size);
        void set_cmd(Command cmd){head()->cmd=cmd;}
       // void set_output_copy(char *, int size);

      //  uint32_t output_length() { return headcontent_buf.iov_len; };

        int collect_output(struct iovec iov[])
        {   int meta_size=meta.ByteSizeLong();
            int cnt=0;
            int ctsize;
            if(meta_size>0){// 3 blocks: head+meta+body
                DLOG("output frame with meta");
                head()->set_meta_bit();
                iov[cnt].iov_len=sizeof(Head);
                iov[cnt++].iov_base=headcontent_buf.iov_base;

                meta_buf.iov_len=meta_size;
                meta_buf.iov_base=malloc(meta_size+2);
                meta.SerializePartialToArray((char*)meta_buf.iov_base+2,meta_size);
                *(uint16_t*)meta_buf.iov_base=meta_size;
                iov[cnt++]=meta_buf;
                if(headcontent_used>sizeof(Head))
                    {iov[cnt].iov_base=(char*)headcontent_buf.iov_base+sizeof(Head);
                    iov[cnt++].iov_len=headcontent_used-sizeof(Head);
                    }
                ctsize=headcontent_used-sizeof(Head)+meta_size+2;
            }else{//1 block
                head()->clear_meta_bit();
                iov[cnt].iov_base=headcontent_buf.iov_base;
                iov[cnt++].iov_len=headcontent_used;
                ctsize=headcontent_used-sizeof(Head);
            }
                for(auto& i:attachment){
                    iov[cnt++]=i;
                    ctsize+=i.iov_len;
                }
                head()->set_content_length(ctsize);
           
            return cnt;
        };
 
        ~Msg()
        {
            //DLOG("cmd:0x%X,user seq:%d,call ~Msg",uint16_t(head()->cmd),head()->request_seq);
            free(headcontent_buf.iov_base);
            free(meta_buf.iov_base);
                for(auto& i:attachment){
                    free(i.iov_base);
                }
        }
    };
    using Request = Msg;

    using Reply =Msg;

}
